/**
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
             佛祖保佑       永无BUG
	佛曰:  
		写字楼里写字间，写字间里程序员；  
		程序人员写程序，又拿程序换酒钱。  
		酒醒只在网上坐，酒醉还来网下眠；  
		酒醉酒醒日复日，网上网下年复年。  
		但愿老死电脑间，不愿鞠躬老板前；  
		奔驰宝马贵者趣，公交自行程序员。  
		别人笑我忒疯癫，我笑自己命太贱；  
		不见满街漂亮妹，哪个归得程序员？
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

*/

package com.fclassroom.logger.service.impl;

import com.fclassroom.logger.dao.ILogDao;
import com.fclassroom.logger.entity.*;
import com.fclassroom.logger.service.ILogService;
import com.sun.management.OperatingSystemMXBean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Jason
 * @date 2016年4月23日 下午1:56:32 Copyright (c) 2016, email:14902300@qq.com All
 *       Rights Reserved.
 */
@Service("logService")
public class LogServiceForDBImpl implements ILogService, InitializingBean {
	protected static final Logger logger = LogManager.getLogger();
	private static Integer LOG_SIZE = 30;

	@Autowired
	private ILogDao logDao;
	@Autowired
	private LogCache logCache;

	@SuppressWarnings("restriction")
	@Override
	public void afterPropertiesSet() throws Exception {
		MathContext mc = new MathContext(2, RoundingMode.HALF_DOWN);
		OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
		// 总的物理内存+虚拟内存
		BigDecimal totalMemory = new BigDecimal(osmxb.getTotalPhysicalMemorySize());

		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					BigDecimal freeMemory = new BigDecimal(osmxb.getFreePhysicalMemorySize());
					Float percent = freeMemory.divide(totalMemory, mc).floatValue();
					if (percent >= 0.50) {
						LOG_SIZE = 500;
					} else if (percent >= 0.30 && percent < 0.50) {
						LOG_SIZE = 300;
					} else if (percent >= 0.15 && percent < 0.30) {
						LOG_SIZE = 200;
					} else {
						LOG_SIZE = 100;
					}
					try {
						Thread.sleep(30000);
					} catch (InterruptedException e) {
						logger.error("Thread.sleep(1000) fail:" + e.getMessage());
					}
				}
			}
		});
		thread.setDaemon(true);
		thread.start();

		List<OperaterLog> operaterLogs1 = new ArrayList<>();
		List<OperaterLog> operaterLogs2 = new ArrayList<>();
		List<OperaterLog> operaterLogs3 = new ArrayList<>();
		List<LogRLogin> logRLoginList = new ArrayList<>();

		Thread dbLogThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						OperaterLog log = logCache.pullOperaterLog();
						if (null != log && null != log.getAppCategory()) {
							switch (log.getAppCategory()) { // 数据库根据AppCategory做了3个分表。
							case 10:
								operaterLogs1.add(log);
								break;
							case 20:
								operaterLogs2.add(log);
								break;
							case 30:
								operaterLogs3.add(log);
								break;
							default:
								break;
							}
							if (operaterLogs1.size() >= LOG_SIZE) {
								logDao.insertOperaterLog(operaterLogs1);
								operaterLogs1.clear();
							}
							if (operaterLogs2.size() >= LOG_SIZE) {
								logDao.insertOperaterLog(operaterLogs2);
								operaterLogs2.clear();
							}
							if (operaterLogs3.size() >= LOG_SIZE) {
								logDao.insertOperaterLog(operaterLogs3);
								operaterLogs3.clear();
							}
						}
						LogRLogin loginLog = logCache.pullLogRLogin();
						if (loginLog != null) {
							logRLoginList.add(loginLog);
						}

						if (logRLoginList.size() >= LOG_SIZE) {
							logDao.insertLoginLog(logRLoginList);
							logRLoginList.clear();
						}
						LogRExamRead examRead = logCache.pullLogRExamRead();
						if (examRead != null && examRead.getExamId() != null) {
							logDao.insertlogRExamRead(examRead);
							logDao.insertlogRExamReadDetailList(examRead.getId(), examRead.getExamReadDetails());
						}
					} catch (Exception e) {
						logger.error("写入登录日志失败:" + e.getMessage());
					}
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						logger.error("Thread.sleep(3000) fail:" + e.getMessage());
					}
				}
			}
		});
		dbLogThread.setDaemon(true);
		dbLogThread.start();

	}

	@Override
	public void insertOperaterLog(OperaterLog operaterLog) {
		logDao.insertOperaterLog(operaterLog);
	}

	@Override
	public void insertSystemLog(SystemLog systemLog) {
		logDao.insertSystemLog(systemLog);
	}

	@Override
	public Map<String, Integer> getReadAndTurnExamCount(Long accountId) throws Exception {
		Map<String, Integer> res = new HashMap<String, Integer>();
		Date now = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("accountId", accountId);
		map.put("startTime", sdf.format(getFirstDateOfMonth(now)));
		map.put("endTime", sdf.format(getLastDateOfMonth(now)));

		Integer readExamCount = logDao.getReadExamCount(map);
		res.put("readExamCount", readExamCount);// 读卷数量
		if (!res.isEmpty()) {
			return res;
		}
		return null;
	}

	@Override
	public List<LogRExamRead> getLogExamRead(Long userId, Long examId, Long schoolId) {
		LogRExamRead log = new LogRExamRead();
		log.setUserId(userId);
		log.setExamId(examId);
		log.setSchoolId(schoolId);
		return logDao.getLogExamRead(log);
	}

	@Override
	public List<LogRExamReadDetail> getLogExamReadDetail(Long logId) {
		LogRExamReadDetail logDetail = new LogRExamReadDetail();
		logDetail.setExamLogId(logId);
		return logDao.getLogExamReadDetail(logDetail);
	}

	@Override
	public LogRScanner saveLogRScanner(Long userId, Long schoolId, Long scannerId, String model, String serialNumber) {
		LogRScanner logScanner = new LogRScanner();
		logScanner.setUserId(userId);
		logScanner.setSchoolId(schoolId);
		logScanner.setScannerId(scannerId);
		logScanner.setModel(model);
		logScanner.setSerialNumber(serialNumber);
		logScanner.setCreateTime(new Date());
		return logDao.saveLogRScanner(logScanner);
	}

	@Override
	public List<LogRExamRead> getLogExamRead(Long schoolId, Long examId, Long userId, String startTime,
			String endTime) {
		LogRExamRead log = new LogRExamRead();
		log.setUserId(userId);
		log.setExamId(examId);
		log.setSchoolId(schoolId);
		log.setStartTime(startTime);
		log.setEndTime(endTime);
		List<LogRExamRead> logs = logDao.getLogExamRead(log);
		for (LogRExamRead e : logs) {
			e.setExamReadDetails(getLogExamReadDetail(e.getId()));
		}
		return logs;
	}

	/**
	 * 获取date所在月的第一天
	 * 
	 * @param date
	 * @return
	 */
	public static Date getFirstDateOfMonth(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		int dayOfMonth = -(cal.get(Calendar.DAY_OF_MONTH) - 1);
		cal.add(Calendar.DATE, dayOfMonth);
		return cal.getTime();
	}

	/**
	 * 获取date所在月的最后一天
	 * 
	 * @param date
	 * @return
	 */
	public static Date getLastDateOfMonth(Date date) {
		Date firstDayOfMonth = getFirstDateOfMonth(date);
		Calendar cal = Calendar.getInstance();
		cal.setTime(firstDayOfMonth);
		cal.add(Calendar.MONTH, 1);
		cal.add(Calendar.DATE, -1);
		return cal.getTime();
	}
}
